﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.NetworkInformation;
using System.Net;

namespace NetworkScanner
{
    public static class MyExtensions
    {
        /// <summary>
        /// Pasar MAC a string agregando el separador dado entre bytes
        /// </summary>
        /// <param name="mac">Direccion fisica</param>
        /// <param name="separador">caracter separador</param>
        /// <returns>MAC separada con el separador dado</returns>
        public static string ToStringSeparated(this PhysicalAddress mac, char separador)
        {
            string Mac = mac.ToString();
            string FormatedMAC = "";
            for (int i = 0; i < Mac.Length; i += 2)
            {
                FormatedMAC += Mac.Substring(i, 2);
                if (i < Mac.Length - 2)
                {
                    FormatedMAC += separador;
                }
            }
            return FormatedMAC;
        }

        /// <summary>
        /// Calcula la direccion de la subred
        /// </summary>
        /// <param name="address">Direccion IP</param>
        /// <param name="subnetMask">Mascara de subred</param>
        /// <returns>Devuelve la direccion de la subred</returns>
        public static IPAddress GetSubnetAddress(this IPAddress address, IPAddress subnetMask)
        {
            byte[] ipAdressBytes = address.GetAddressBytes();
            byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
            byte[] SubnetAddress = new byte[ipAdressBytes.Length];
            for (int i = 0; i < SubnetAddress.Length; i++)
            {
                SubnetAddress[i] = (byte)(ipAdressBytes[i] & subnetMaskBytes[i]);
            }
            return new IPAddress(SubnetAddress);
        }


        /// <summary>
        /// Convierte una direccion IP a su equibalente entero de 32 bits
        /// </summary>
        /// <param name="ip">Direccion IP</param>
        /// <returns>Entero de 32 bits</returns>
        public static int IPtoInt(this IPAddress ip)
        {
            uint dir = 0;
            byte[] ipbytes = ip.GetAddressBytes();
            for (int i = 0; i < ipbytes.Length; i++)
            {
                dir += (uint)(Math.Pow(256, i) * ipbytes[i]);
            }
            return (int)dir;
        }


        /// <summary>
        /// Dada la mascara calcula el tamaño de la red (cantidad de hosts posibles)
        /// </summary>
        /// <param name="Mask">mascara de subred</param>
        /// <returns>cantidad de hosts posibles</returns>
        public static uint subnetSize(this IPAddress Mask)
        {
            return (uint)Math.Pow(2, 32 - PrefixLength(Mask));
        }

        /// <summary>
        /// Devuelve la longitud del prefijo (numero de bits en 1 de la mascara dada)
        /// </summary>
        /// <param name="Mask">mascara de subred</param>
        /// <returns>cantidad de bits en 1 de la maascara</returns>
        public static int PrefixLength(this IPAddress Mask)
        {
            int cant = 0, neg = 0;
            byte[] maskBytes = Mask.GetAddressBytes();

            for (int i = 0; i < maskBytes.Length; i++)
            {
                neg = (int)(byte.MaxValue - maskBytes[i] + 1);
                cant += (int)Math.Log(neg, 2);
            }
            return (32 - cant);
        }

        /// <summary>
        /// Metodo extension de IPAddress para comparar la cardinalidad de la direccion IP
        /// </summary>
        /// <param name="ip1">Esta IP</param>
        /// <param name="ip2">Otra con quien comparar</param>
        /// <returns>-1, 1, 0 segun sea menor, mayor o igual respectivamente</returns>
        public static int CompareTo(this IPAddress ip1, IPAddress ip2)
        {
            byte[] byteIP1 = ip1.GetAddressBytes();
            byte[] byteIP2 = ip2.GetAddressBytes();
            for (int i = 0; i < byteIP1.Length; i++)
            {
                if (byteIP1[i] < byteIP2[i]) return -1;
                else if (byteIP1[i] > byteIP2[i]) return 1;
            }
            return 0;
        }

        /// <summary>
        /// Cantidad de direcciones entre ip1 e ip2
        /// </summary>
        /// <param name="ip1">mayor ip</param>
        /// <param name="ip2">menor ip</param>
        /// <returns>ip1 - ip2</returns>
        public static uint diferenciaCon(this IPAddress ip1, IPAddress ip2)
        {
            byte[] byteIP1 = ip1.GetAddressBytes();
            byte[] byteIP2 = ip2.GetAddressBytes();
            uint diferencia = 0;
            for (int i = 0; i < byteIP1.Length; i++)
            {
                diferencia += (uint)(Math.Pow(256, 3 - i) * (byteIP1[i] - byteIP2[i]));
            }
            return diferencia;
        }


        /// <summary>
        /// Incrementar IP en un numero de direcciones dadas
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="incremento">incremento a producir</param>
        /// <returns>nueva IP incrementada segun parametro</returns>
        public static IPAddress Incrementar(this IPAddress ip, uint incremento)
        {
            byte[] ipbytes = ip.GetAddressBytes();
            const uint base256 = 256;
            uint suma = 0;
            bool carry = false;

            for (int i = ipbytes.Length - 1; i >= 0; i--)
            {
                suma = incremento % base256;
                suma += carry ? (uint)1 : (uint)0;
                carry = ((suma + ipbytes[i]) >= base256);
                ipbytes[i] = (byte)((suma + ipbytes[i]) % base256);
                incremento = incremento / base256;
                if (incremento == 0 && !carry) break;
            }
            if (carry) { throw new OverflowException(); }
            return (new IPAddress(ipbytes));
        }

        /// <summary>
        /// Resuelve si una direccion IP pertenece a la red dada la mascara
        /// </summary>
        /// <param name="ip">ip a comprobar</param>
        /// <param name="Red">red</param>
        /// <param name="Mask">mascara</param>
        /// <returns>verdadero si pertenece, falso si no</returns>
        public static bool IsLAN(this IPAddress ip, IPAddress Red, IPAddress Mask)
        {
            byte[] ipbytes = ip.GetAddressBytes();
            byte[] Redbytes = Red.GetAddressBytes();
            byte[] Maskbytes = Mask.GetAddressBytes();
            bool resultado = true;
            for (int i = 0; i < ipbytes.Length && resultado; i++)
            {
                resultado = ((byte)ipbytes[i] & (byte)Maskbytes[i]) == (byte)Redbytes[i];
            }
            return resultado;
        }

    }
}
